#include "objloader.h"
#include "helper.h"
#include <stdlib.h>

ObjLoader::ObjLoader() {
	ZeroMemory(dir, MAX_PATH);

}

ObjLoader::~ObjLoader() {
	pod_materials.clear();
	pod_indices.clear();
	pod_attributes.clear();
	pod_vertices.clear();
}

bool ObjLoader::InitialiseModel(WCHAR *dir, WCHAR *model) {
	wcscpy_s(this->dir, MAX_PATH, dir);
	if (!LoadObj(model))
		return false;

	return true;
}


bool ObjLoader::LoadObj(WCHAR *obj) {
	using std::wifstream;
	using std::vector;

	WCHAR programDir[MAX_PATH] = { 0 };
	GetCurrentDirectory(MAX_PATH - 1, programDir);
	SetCurrentDirectory(this->dir);

	wifstream file;
	file.open(obj, std::wifstream::in);

	if (!file.is_open()) {
		return Error(L"Could not open obj file", L"Input Error");
	}

	WCHAR materialFile[MAX_PATH] = { 0 };

	// temporary vars
	vector<D3DXVECTOR3> positions;
	vector<D3DXVECTOR2> texcoords;
	vector<D3DXVECTOR3> normals;

	UINT subset = 0;
	pod::Material *pod_material = new pod::Material;

	// obj file indices start at 1, fill the zeroes
	positions.push_back(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
	normals.push_back(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
	texcoords.push_back(D3DXVECTOR2(0.0f, 0.0f));

	WCHAR line[MAX_PATH] = { 0 };
	WCHAR cmd[MAX_PATH] = { 0 };

	while (file.good()) {
		file >> cmd;
		file.ignore(1); // skip the empty char

		// comment and empty lines
		if (cmd[0] == L'#' || cmd[0] == L'') {
			file.ignore(MAX_PATH, L'\n');
			continue;
		}

		// vertices
		if (0 == wcsncmp(cmd, L"v", 2)) {
			float x, y, z;
			file >> x >> y >> z;
			positions.push_back(D3DXVECTOR3(x, y, z));
		}
		// texcords
		else if (0 == wcsncmp(cmd, L"vt", 2)) {
			float u, v;
			file >> u >> v;
			texcoords.push_back(D3DXVECTOR2(u, v));
		}
		// normals
		else if (0 == wcsncmp(cmd, L"vn", 2)) {
			float x, y, z;
			file >> x >> y >> z;
			normals.push_back(D3DXVECTOR3(x, y, z));
		}
		// faces
		else if (0 == wcsncmp(cmd, L"f", 2)) {
			UINT v, vn, vt;
			WCHAR face[MAX_PATH] = { 0 };
			ptn::Vertex vertex;
			for (UINT i = 0; i < 3; i++) {
				file >> face;
				swscanf_s(face, L"%d/%d/%d", &v, &vt, &vn);
				vertex = ptn::CreateVertex(positions[v], texcoords[vt],
						normals[vn]);
				DWORD index = addVertex(v - 1, &vertex);
				if (index == (DWORD) -1)
					return Error(L"Out of memory");
				//pod_vertices.push_back(ptn::CreateVertex(positions[v], texcoords[vt], normals[vn]));
				//pod_indices.push_back(pod_vertices.size() -1);
				pod_indices.push_back(index);
			}
			pod_attributes.push_back(subset);
		}
		// material name
		else if (0 == wcsncmp(cmd, L"usemtl", 2)) {
			WCHAR name[MAX_PATH] = { 0 };
			file.getline(name, MAX_PATH - 1, L'\n');

			bool found = false;
			for (UINT i = 0; i < pod_materials.size(); i++) {
				pod::Material *temp = pod_materials[i];
				if (0 == wcsncmp(name, temp->name, wcslen(name))) {
					subset = i;
					found = true;
					break;
				}
			}

			if (!found) {
				pod_material = new pod::Material;
				wcsncpy_s(pod_material->name, name, MAX_PATH);
				pod_materials.push_back(pod_material);
				subset = pod_materials.size() - 1;
			}
		}
		// material file
		else if (0 == wcsncmp(cmd, L"mtllib", 2)) {
			file.getline(materialFile, MAX_PATH - 1, L'\n');
		}
		// skip all unhandled  tags
		else {
			file.ignore(MAX_PATH, L'\n');
		}
	}

	file.close();

	// cleanup cache
	for (UINT i = 0; i < cache.size(); ++i) {
		Cache *entry = cache[i];
		while (entry != NULL) {
			Cache *next = entry->next;
			if (entry != NULL) {
				delete entry;
				entry = next;
			}
		}
	}

	cache.clear();
	positions.clear();
	normals.clear();
	texcoords.clear();

	if (!LoadMtl(materialFile))
		return false;

	SetCurrentDirectory(programDir);

	return true;
}

bool ObjLoader::LoadMtl(WCHAR *mtl) {
	using std::wifstream;

	wifstream file;
	file.open(mtl, std::wifstream::in);

	if (!file.is_open())
		return Error(L"Could not open .mtl file", L"Input error!");

	WCHAR line[MAX_PATH] = { 0 };
	WCHAR cmd[MAX_PATH] = { 0 };

	pod::Material *pod_material = 0;
	while (file.good()) {
		file >> cmd;
		file.ignore(1);

		if (cmd[0] == L'#' || cmd[0] == L'') {
			file.ignore(MAX_PATH, L'\n');
			continue;
		}

		if (0 == wcsncmp(cmd, L"newmtl", 2)) {
			file >> line;
			bool found = false;
			for (UINT i = 0; i < pod_materials.size(); ++i) {
				pod::Material *temp = pod_materials[i];
				if (0 == wcsncmp(line, temp->name, wcslen(temp->name))) {
					pod_material = pod_materials[i];
					found = true;
					break;
				}
			}

			if (!found) {
				pod_material = new pod::Material;
				wcsncpy_s(pod_material->name, line, MAX_PATH);
				pod_materials.push_back(pod_material);
			}
		}

		else if (0 == wcsncmp(cmd, L"Ka", 2)) {
			float x, y, z;
			file >> x >> y >> z;
			pod_material->ambient = D3DXVECTOR4(x, y, z, 1.0f);
		}

		else if (0 == wcsncmp(cmd, L"Kd", 2)) {
			float x, y, z;
			file >> x >> y >> z;
			pod_material->diffuse = D3DXVECTOR4(x, y, z, 1.0f);
		}

		else if (0 == wcsncmp(cmd, L"Ks", 2)) {
			float x, y, z;
			file >> x >> y >> z;
			pod_material->specular = D3DXVECTOR4(x, y, z, 1.0f);
		}
		// Shininess(specular power)
		else if (0 == wcsncmp(cmd, L"Ns", 2)) {
			float specularPower;
			file >> specularPower;
			pod_material->specularPower = specularPower;
		}

		else if (0 == wcsncmp(cmd, L"d", 2) || 0 == wcsncmp(cmd, L"Tr", 2)) {
			float alpha;
			file >> alpha;

			pod_material->alpha = alpha;
		}

		else if (0 == wcsncmp(cmd, L"illum", 2)) {
			int illum;
			file >> illum;
			pod_material->illum = illum;
		}

		else if (0 == wcsncmp(cmd, L"map_Kd", 2)) {
			file.getline(line, MAX_PATH - 1, L'\n');
			if (0 != wcsncmp(line, L"", 3)) {
				WCHAR fullpath[MAX_PATH] = { 0 };
				wcscpy_s(fullpath, MAX_PATH - 1, dir);
				wcscat_s(fullpath, MAX_PATH - 1, L"\\");
				wcscat_s(fullpath, MAX_PATH - 1, line);
				wcsncpy_s(pod_material->diffuseMap, fullpath, MAX_PATH);
				pod_material->isTextured = true;
			}
		}

		else {
			file.ignore(MAX_PATH, L'\n');
		}
	}

	file.close();

	return true;
}

DWORD ObjLoader::addVertex(UINT hash, ptn::Vertex *v) {
	UINT index = 0;
	bool found = false;

	if (cache.size() > hash) {
		Cache *entry = cache[hash];
		while (entry != NULL) {
			ptn::Vertex temp = pod_vertices[entry->index];
			if (0 == memcmp(v, &temp, sizeof(ptn::Vertex))) {
				found = true;
				index = entry->index;
				break;
			}

			entry = entry->next;
		}
	}

	if (!found) {
		index = pod_vertices.size();
		pod_vertices.push_back(*v);

		Cache* newEntry = new Cache;
		if (newEntry == NULL) {
			return (DWORD) -1;
		}

		newEntry->index = index;
		newEntry->next = NULL;

		// resize
		while (cache.size() <= hash) {
			cache.push_back(NULL);
		}

		// add to the end
		Cache *curEntry = cache[hash];
		if (curEntry == NULL)
			cache[hash] = newEntry;

		else {
			while (curEntry->next != NULL) {
				curEntry = curEntry->next;
			}

			curEntry->next = newEntry;
		}
	}

	return index;
}
